﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Input;
using Socium.Domain.Collections;
using Socium.Domain.Persons;
using Socium.Presentation;

namespace Socium.Persons {
	sealed class GroupTreeItem : NamedViewModel {
		readonly Group _group;
		readonly ICollection<Group> _groups;
		readonly ICollection<Person> _persons;

		#region Constructors

		GroupTreeItem(string name, Group group, ICollection<Group> groups, ICollection<Person> persons) {
			_group = group;
			_groups = groups;
			_persons = persons;

			InitializeItemsCollection();
			InitializeItems();
			InitializePersons(persons);

			NewGroupCommand = new Command<EnvironmentService>(OnNewGroup);
			DeleteCommand = new Command<EnvironmentService>(OnDelete);
			DetailsCommand = new Command<EnvironmentService>(OnDetails);

			NewPersonCommand = new Command<EnvironmentService>(OnNewPerson);

			DisplayName = name;
		}

		public GroupTreeItem(string name, ICollection<Group> groups, ICollection<Person> persons) : this(name, null, groups, persons) {}
		public GroupTreeItem(Group group) : this(group.ToString(), group, group.Groups, group.Persons) { }

		#endregion

		#region IsFixed

		public bool IsFixed {
			get { return _group == null; }
		}

		#endregion

		#region Items

		readonly ObservableCollection<GroupTreeItem> _items = new ObservableCollection<GroupTreeItem>();

		public IEnumerable<GroupTreeItem> Items {
			get { return _items; }
		}

		void InitializeItemsCollection() {
			_items.CollectionChanged += ItemsCollectionChanged;
		}

		void ItemsCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) {
			if (e.OldItems != null) {
				foreach (GroupTreeItem old_item in e.OldItems) {
					old_item.SetParent(null);
				}
			}

			if (e.NewItems != null) {
				foreach (GroupTreeItem new_item in e.NewItems) {
					new_item.SetParent(this);
				}
			}
		}

		void InitializeItems() {
			var items =
				from @group in _groups
				select new GroupTreeItem(@group);

			_items.AddRange(items);
		}

		void Add(GroupTreeItem item) {
			_groups.Add(item._group);
			_items.Add(item);
		}

		void Remove(GroupTreeItem item) {
			_groups.Remove(item._group);
			_items.Remove(item);
		}

		#endregion

		#region Persons

		readonly ObservableCollection<PersonLineItem> _person_line_items = new ObservableCollection<PersonLineItem>();

		public IEnumerable<PersonLineItem> Persons {
			get { return _person_line_items; }
		}

		void InitializePersons(IEnumerable<Person> persons) {
			_person_line_items.AddRange(persons.MapLazy(person => new PersonLineItem(person)));
		}

		public void Add(PersonLineItem item) {
			var person = item.Model;
			for (var group = this; group != null; group = group.Parent) {
				var persons = group._persons;
				if (!persons.Contains(person)) {
					persons.Add(person);
					group._person_line_items.Add(item);
				}
			}
		}

		public void Remove(PersonLineItem item) {
			foreach (var child in _items) {
				child.Remove(item);
			}

			_group.Persons.Remove(item.Model);
			_person_line_items.Remove(item);
		}

		public bool Contains(PersonLineItem item) {
			return _person_line_items.Contains(item);
		}

		#endregion

		#region Parent

		GroupTreeItem _parent;

		public GroupTreeItem Parent {
			get { return _parent; }
			set {
				if (_parent == value) {
					return;
				}

				if (_parent != null) {
					_parent.Remove(this);
				}

				if (value != null) {
					value.Add(this);
				}
			}
		}

		void SetParent(GroupTreeItem value) {
			_parent = value;

			OnPropertyChanged("Parent");
			OnPropertyChanged("IsRoot");
		}

		#endregion

		#region IsRoot

		public bool IsRoot {
			get { return _parent == null; }
		}

		#endregion

		#region New group

		public ICommand NewGroupCommand { get; private set; }

		void OnNewGroup(EnvironmentService environment_service) {
			var view_model = new GroupViewModel();
			view_model.ModelCreated += @group => NewGroupCreated(view_model, @group);

			environment_service.ShowForm(view_model);
		}

		void NewGroupCreated(GroupViewModel view_model, Group @group) {
			var item = new GroupTreeItem(@group);
			Add(item);

			view_model.ModelUpdated += item.Update;
		}

		#endregion

		#region New person

		public ICommand NewPersonCommand { get; private set; }

		void OnNewPerson(EnvironmentService environment_service) {
			var view_model = new PersonViewModel();
			view_model.ModelCreated += person => {
				var item = new PersonLineItem(person);
				Add(item);

				view_model.ModelUpdated += item.Update;
			};

			environment_service.ShowForm(view_model);
		}

		#endregion

		#region Update

		void Update() {
			if (_group != null) {
				DisplayName = _group.Name;
			}
		}

		#endregion

		#region Delete

		public ICommand DeleteCommand { get; private set; }

		void OnDelete(EnvironmentService environment_service) {
			if (_group == null || _parent == null) {
				throw new InvalidOperationException();
			}

			if (environment_service.Confirm("Do you realy want to delete?")) {
				_parent.Remove(this);
			}
		}

		#endregion

		#region Details

		public ICommand DetailsCommand { get; private set; }

		void OnDetails(EnvironmentService environment_service) {
			var view_model = new GroupViewModel(_group);
			view_model.ModelUpdated += Update;
			environment_service.ShowForm(view_model);
		}

		#endregion
	}
}